update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inline / InlineMethodProcessor.java
blob2ea7b0214087d6456fb3f0a5f50e274db605e76e
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.refactoring.inline;
18 import com.intellij.codeInsight.ChangeContextUtil;
19 import com.intellij.history.LocalHistory;
20 import com.intellij.history.LocalHistoryAction;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.editor.LogicalPosition;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.util.Ref;
27 import com.intellij.psi.*;
28 import com.intellij.psi.codeStyle.CodeStyleManager;
29 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
30 import com.intellij.psi.codeStyle.VariableKind;
31 import com.intellij.psi.controlFlow.*;
32 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
33 import com.intellij.psi.infos.MethodCandidateInfo;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.searches.ReferencesSearch;
36 import com.intellij.psi.util.InheritanceUtil;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.psi.util.PsiTypesUtil;
39 import com.intellij.psi.util.PsiUtil;
40 import com.intellij.refactoring.BaseRefactoringProcessor;
41 import com.intellij.refactoring.RefactoringBundle;
42 import com.intellij.refactoring.introduceParameter.Util;
43 import com.intellij.refactoring.rename.RenameJavaVariableProcessor;
44 import com.intellij.refactoring.ui.ConflictsDialog;
45 import com.intellij.refactoring.util.*;
46 import com.intellij.usageView.UsageInfo;
47 import com.intellij.usageView.UsageViewDescriptor;
48 import com.intellij.usageView.UsageViewUtil;
49 import com.intellij.util.IncorrectOperationException;
50 import com.intellij.util.containers.HashMap;
51 import com.intellij.util.containers.MultiMap;
52 import org.jetbrains.annotations.NonNls;
53 import org.jetbrains.annotations.NotNull;
54 import org.jetbrains.annotations.Nullable;
56 import java.util.*;
58 public class InlineMethodProcessor extends BaseRefactoringProcessor {
59 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineMethodProcessor");
61 private PsiMethod myMethod;
62 private PsiJavaCodeReferenceElement myReference;
63 private final Editor myEditor;
64 private final boolean myInlineThisOnly;
66 private final PsiManager myManager;
67 private final PsiElementFactory myFactory;
68 private final CodeStyleManager myCodeStyleManager;
69 private final JavaCodeStyleManager myJavaCodeStyle;
71 private PsiBlockStatement[] myAddedBraces;
72 private final String myDescriptiveName;
73 private Map<PsiField, PsiClassInitializer> myAddedClassInitializers;
74 private PsiMethod myMethodCopy;
76 public InlineMethodProcessor(@NotNull Project project,
77 @NotNull PsiMethod method,
78 @Nullable PsiJavaCodeReferenceElement reference,
79 Editor editor,
80 boolean isInlineThisOnly) {
81 super(project);
82 myMethod = method;
83 myReference = reference;
84 myEditor = editor;
85 myInlineThisOnly = isInlineThisOnly;
87 myManager = PsiManager.getInstance(myProject);
88 myFactory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
89 myCodeStyleManager = CodeStyleManager.getInstance(myProject);
90 myJavaCodeStyle = JavaCodeStyleManager.getInstance(myProject);
91 myDescriptiveName = UsageViewUtil.getDescriptiveName(myMethod);
94 protected String getCommandName() {
95 return RefactoringBundle.message("inline.method.command", myDescriptiveName);
98 protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
99 return new InlineViewDescriptor(myMethod);
102 @NotNull
103 protected UsageInfo[] findUsages() {
104 if (myInlineThisOnly) return new UsageInfo[]{new UsageInfo(myReference)};
105 Set<UsageInfo> usages = new HashSet<UsageInfo>();
106 if (myReference != null) {
107 usages.add(new UsageInfo(myReference));
109 for (PsiReference reference : ReferencesSearch.search(myMethod)) {
110 usages.add(new UsageInfo(reference.getElement()));
113 return usages.toArray(new UsageInfo[usages.size()]);
116 protected void refreshElements(PsiElement[] elements) {
117 boolean condition = elements.length == 1 && elements[0] instanceof PsiMethod;
118 LOG.assertTrue(condition);
119 myMethod = (PsiMethod)elements[0];
122 protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
123 UsageInfo[] usagesIn = refUsages.get();
124 MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
126 if (!myInlineThisOnly) {
127 final PsiMethod[] superMethods = myMethod.findSuperMethods();
128 for (PsiMethod method : superMethods) {
129 final String message = method.hasModifierProperty(PsiModifier.ABSTRACT) ? RefactoringBundle
130 .message("inlined.method.implements.method.from.0", method.getContainingClass().getQualifiedName()) : RefactoringBundle
131 .message("inlined.method.overrides.method.from.0", method.getContainingClass().getQualifiedName());
132 conflicts.putValue(method, message);
136 addInaccessibleMemberConflicts(myMethod, usagesIn, new ReferencedElementsCollector(), conflicts);
138 if (!conflicts.isEmpty()) {
139 ConflictsDialog dialog = new ConflictsDialog(myProject, conflicts);
140 dialog.show();
141 if (!dialog.isOK()) {
142 if (dialog.isShowConflicts()) prepareSuccessful();
143 return false;
147 if (!myInlineThisOnly) {
148 if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, myMethod)) return false;
151 prepareSuccessful();
152 return true;
155 public static void addInaccessibleMemberConflicts(final PsiElement element,
156 final UsageInfo[] usages,
157 final ReferencedElementsCollector collector,
158 final MultiMap<PsiElement, String> conflicts) {
159 element.accept(collector);
160 final Map<PsiMember, Set<PsiMember>> containersToReferenced = getInaccessible(collector.myReferencedMembers, usages);
162 final Set<PsiMember> containers = containersToReferenced.keySet();
163 for (PsiMember container : containers) {
164 Set<PsiMember> referencedInaccessible = containersToReferenced.get(container);
165 for (PsiMember referenced : referencedInaccessible) {
166 final String referencedDescription = RefactoringUIUtil.getDescription(referenced, true);
167 final String containerDescription = RefactoringUIUtil.getDescription(container, true);
168 String message = RefactoringBundle.message("0.that.is.used.in.inlined.method.is.not.accessible.from.call.site.s.in.1",
169 referencedDescription, containerDescription);
170 conflicts.putValue(container, CommonRefactoringUtil.capitalize(message));
176 * Given a set of referencedElements, returns a map from containers (in a sense of ConflictsUtil.getContainer)
177 * to subsets of referencedElemens that are not accessible from that container
179 * @param referencedElements
180 * @param usages
182 private static Map<PsiMember, Set<PsiMember>> getInaccessible(HashSet<PsiMember> referencedElements, UsageInfo[] usages) {
183 Map<PsiMember, Set<PsiMember>> result = new HashMap<PsiMember, Set<PsiMember>>();
185 for (UsageInfo usage : usages) {
186 final PsiMember container = ConflictsUtil.getContainer(usage.getElement());
187 if (container == null) continue; // usage in import statement
188 Set<PsiMember> inaccessibleReferenced = result.get(container);
189 if (inaccessibleReferenced == null) {
190 inaccessibleReferenced = new HashSet<PsiMember>();
191 result.put(container, inaccessibleReferenced);
192 for (PsiMember member : referencedElements) {
193 if (!PsiUtil.isAccessible(member, usage.getElement(), null)) {
194 inaccessibleReferenced.add(member);
200 return result;
203 protected void performRefactoring(UsageInfo[] usages) {
204 int col = -1;
205 int line = -1;
206 if (myEditor != null) {
207 col = myEditor.getCaretModel().getLogicalPosition().column;
208 line = myEditor.getCaretModel().getLogicalPosition().line;
209 LogicalPosition pos = new LogicalPosition(0, 0);
210 myEditor.getCaretModel().moveToLogicalPosition(pos);
213 LocalHistoryAction a = LocalHistory.startAction(myProject, getCommandName());
214 try {
215 doRefactoring(usages);
217 finally {
218 a.finish();
221 if (myEditor != null) {
222 LogicalPosition pos = new LogicalPosition(line, col);
223 myEditor.getCaretModel().moveToLogicalPosition(pos);
227 private void doRefactoring(UsageInfo[] usages) {
228 try {
229 if (myInlineThisOnly) {
230 if (myMethod.isConstructor()) {
231 PsiCall constructorCall = RefactoringUtil.getEnclosingConstructorCall(myReference);
232 if (constructorCall != null) {
233 inlineConstructorCall(constructorCall);
236 else {
237 myReference = addBracesWhenNeeded(new PsiReferenceExpression[]{(PsiReferenceExpression)myReference})[0];
238 inlineMethodCall((PsiReferenceExpression)myReference);
241 else {
242 RefactoringUtil.sortDepthFirstRightLeftOrder(usages);
243 if (myMethod.isConstructor()) {
244 for (UsageInfo usage : usages) {
245 PsiElement element = usage.getElement();
246 if (element instanceof PsiJavaCodeReferenceElement) {
247 PsiCall constructorCall = RefactoringUtil.getEnclosingConstructorCall((PsiJavaCodeReferenceElement)element);
248 if (constructorCall != null) {
249 inlineConstructorCall(constructorCall);
252 else if (element instanceof PsiEnumConstant) {
253 inlineConstructorCall((PsiEnumConstant) element);
256 myMethod.delete();
258 else {
259 List<PsiReferenceExpression> refExprList = new ArrayList<PsiReferenceExpression>();
260 for (final UsageInfo usage : usages) {
261 final PsiElement element = usage.getElement();
262 if (element instanceof PsiReferenceExpression) {
263 refExprList.add((PsiReferenceExpression)element);
266 PsiReferenceExpression[] refs = refExprList.toArray(new PsiReferenceExpression[refExprList.size()]);
267 refs = addBracesWhenNeeded(refs);
268 for (PsiReferenceExpression ref : refs) {
269 inlineMethodCall(ref);
271 myMethod.delete();
274 removeAddedBracesWhenPossible();
276 catch (IncorrectOperationException e) {
277 LOG.error(e);
281 public static void inlineConstructorCall(PsiCall constructorCall) {
282 final PsiMethod oldConstructor = constructorCall.resolveMethod();
283 LOG.assertTrue(oldConstructor != null);
284 final PsiExpression[] instanceCreationArguments = constructorCall.getArgumentList().getExpressions();
285 final PsiParameter[] parameters = oldConstructor.getParameterList().getParameters();
286 LOG.assertTrue(parameters.length == instanceCreationArguments.length);
288 PsiStatement[] statements = oldConstructor.getBody().getStatements();
289 LOG.assertTrue(statements.length == 1 && statements[0] instanceof PsiExpressionStatement);
290 PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
291 LOG.assertTrue(expression instanceof PsiMethodCallExpression);
292 ChangeContextUtil.encodeContextInfo(expression, true);
294 PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression.copy();
295 final PsiExpression[] args = methodCall.getArgumentList().getExpressions();
296 for (PsiExpression arg : args) {
297 replaceParameterReferences(arg, oldConstructor, instanceCreationArguments);
300 try {
301 final PsiExpressionList exprList = (PsiExpressionList) constructorCall.getArgumentList().replace(methodCall.getArgumentList());
302 ChangeContextUtil.decodeContextInfo(exprList, PsiTreeUtil.getParentOfType(constructorCall, PsiClass.class), null);
304 catch (IncorrectOperationException e) {
305 LOG.error(e);
307 ChangeContextUtil.clearContextInfo(expression);
310 private static void replaceParameterReferences(final PsiElement element,
311 final PsiMethod oldConstructor,
312 final PsiExpression[] instanceCreationArguments) {
313 boolean isParameterReference = false;
314 if (element instanceof PsiReferenceExpression) {
315 final PsiReferenceExpression expression = (PsiReferenceExpression)element;
316 PsiElement resolved = expression.resolve();
317 if (resolved instanceof PsiParameter &&
318 element.getManager().areElementsEquivalent(((PsiParameter)resolved).getDeclarationScope(), oldConstructor)) {
319 isParameterReference = true;
320 PsiElement declarationScope = ((PsiParameter)resolved).getDeclarationScope();
321 PsiParameter[] declarationParameters = ((PsiMethod)declarationScope).getParameterList().getParameters();
322 for (int j = 0; j < declarationParameters.length; j++) {
323 if (declarationParameters[j] == resolved) {
324 try {
325 expression.replace(instanceCreationArguments[j]);
327 catch (IncorrectOperationException e) {
328 LOG.error(e);
334 if (!isParameterReference) {
335 PsiElement child = element.getFirstChild();
336 while (child != null) {
337 PsiElement next = child.getNextSibling();
338 replaceParameterReferences(child, oldConstructor, instanceCreationArguments);
339 child = next;
344 private void inlineMethodCall(PsiReferenceExpression ref) throws IncorrectOperationException {
345 InlineMethodHandler.TailCallType tailCall = InlineMethodHandler.getTailCallType(ref);
346 ChangeContextUtil.encodeContextInfo(myMethod, false);
347 myMethodCopy = (PsiMethod)myMethod.copy();
348 ChangeContextUtil.clearContextInfo(myMethod);
350 PsiMethodCallExpression methodCall = (PsiMethodCallExpression)ref.getParent();
352 PsiSubstitutor callSubstitutor = getCallSubstitutor(methodCall);
353 BlockData blockData = prepareBlock(ref, callSubstitutor, methodCall.getArgumentList(), tailCall);
354 solveVariableNameConflicts(blockData.block, ref);
355 if (callSubstitutor != PsiSubstitutor.EMPTY) {
356 substituteMethodTypeParams(blockData.block, callSubstitutor);
358 addParmAndThisVarInitializers(blockData, methodCall);
360 PsiElement anchor = RefactoringUtil.getParentStatement(methodCall, true);
361 if (anchor == null) {
362 PsiEnumConstant enumConstant = PsiTreeUtil.getParentOfType(methodCall, PsiEnumConstant.class);
363 if (enumConstant != null) {
364 PsiExpression returnExpr = getSimpleReturnedExpression(myMethod);
365 if (returnExpr != null) {
366 methodCall.replace(returnExpr);
369 return;
371 PsiElement anchorParent = anchor.getParent();
372 PsiLocalVariable thisVar = null;
373 PsiLocalVariable[] parmVars = new PsiLocalVariable[blockData.parmVars.length];
374 PsiLocalVariable resultVar = null;
375 PsiStatement[] statements = blockData.block.getStatements();
376 if (statements.length > 0) {
377 int last = statements.length - 1;
378 /*PsiElement first = statements[0];
379 PsiElement last = statements[statements.length - 1];*/
381 if (statements.length > 0 && statements[statements.length - 1] instanceof PsiReturnStatement &&
382 tailCall != InlineMethodHandler.TailCallType.Return) {
383 last--;
386 int first = 0;
387 if (first <= last) {
388 final PsiElement rBraceOrReturnStatement =
389 PsiTreeUtil.skipSiblingsForward(statements[last], PsiWhiteSpace.class, PsiComment.class);
390 LOG.assertTrue(rBraceOrReturnStatement != null);
391 final PsiElement beforeRBraceStatement = rBraceOrReturnStatement.getPrevSibling();
392 LOG.assertTrue(beforeRBraceStatement != null);
393 PsiElement firstAdded = anchorParent.addRangeBefore(statements[first], beforeRBraceStatement, anchor);
395 PsiElement current = firstAdded.getPrevSibling();
396 LOG.assertTrue(current != null);
397 if (blockData.thisVar != null) {
398 PsiDeclarationStatement statement = PsiTreeUtil.getNextSiblingOfType(current, PsiDeclarationStatement.class);
399 thisVar = (PsiLocalVariable)statement.getDeclaredElements()[0];
400 current = statement;
402 for (int i = 0; i < parmVars.length; i++) {
403 PsiDeclarationStatement statement = PsiTreeUtil.getNextSiblingOfType(current, PsiDeclarationStatement.class);
404 parmVars[i] = (PsiLocalVariable)statement.getDeclaredElements()[0];
405 current = statement;
407 if (blockData.resultVar != null) {
408 PsiDeclarationStatement statement = PsiTreeUtil.getNextSiblingOfType(current, PsiDeclarationStatement.class);
409 resultVar = (PsiLocalVariable)statement.getDeclaredElements()[0];
412 if (statements.length > 0) {
413 final PsiStatement lastStatement = statements[statements.length - 1];
414 if (lastStatement instanceof PsiReturnStatement && tailCall != InlineMethodHandler.TailCallType.Return) {
415 final PsiExpression returnValue = ((PsiReturnStatement)lastStatement).getReturnValue();
416 if (returnValue != null && PsiUtil.isStatement(returnValue)) {
417 PsiExpressionStatement exprStatement = (PsiExpressionStatement)myFactory.createStatementFromText("a;", null);
418 exprStatement.getExpression().replace(returnValue);
419 anchorParent.addBefore(exprStatement, anchor);
425 if (methodCall.getParent() instanceof PsiExpressionStatement || tailCall == InlineMethodHandler.TailCallType.Return) {
426 methodCall.getParent().delete();
428 else {
429 if (blockData.resultVar != null) {
430 PsiExpression expr = myFactory.createExpressionFromText(blockData.resultVar.getName(), null);
431 methodCall.replace(expr);
433 else {
434 //??
438 PsiClass thisClass = myMethod.getContainingClass();
439 PsiExpression thisAccessExpr;
440 if (thisVar != null) {
441 if (!canInlineParmOrThisVariable(thisVar)) {
442 thisAccessExpr = myFactory.createExpressionFromText(thisVar.getName(), null);
444 else {
445 thisAccessExpr = thisVar.getInitializer();
448 else {
449 thisAccessExpr = null;
451 ChangeContextUtil.decodeContextInfo(anchorParent, thisClass, thisAccessExpr);//todo super should be encoded decoded as well
453 if (thisVar != null) {
454 inlineParmOrThisVariable(thisVar, false);
456 final PsiParameter[] parameters = myMethod.getParameterList().getParameters();
457 for (int i = 0; i < parmVars.length; i++) {
458 final PsiParameter parameter = parameters[i];
459 final boolean strictlyFinal = parameter.hasModifierProperty(PsiModifier.FINAL) && isStrictlyFinal(parameter);
460 inlineParmOrThisVariable(parmVars[i], strictlyFinal);
462 if (resultVar != null) {
463 inlineResultVariable(resultVar);
466 ChangeContextUtil.clearContextInfo(anchorParent);
469 private PsiSubstitutor getCallSubstitutor(PsiMethodCallExpression methodCall) {
470 JavaResolveResult resolveResult = methodCall.getMethodExpression().advancedResolve(false);
471 LOG.assertTrue(myManager.areElementsEquivalent(resolveResult.getElement(), myMethod));
472 if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
473 Iterator<PsiTypeParameter> oldTypeParameters = PsiUtil.typeParametersIterator(myMethod);
474 Iterator<PsiTypeParameter> newTypeParameters = PsiUtil.typeParametersIterator(myMethodCopy);
475 PsiSubstitutor substitutor = resolveResult.getSubstitutor();
476 while (newTypeParameters.hasNext()) {
477 final PsiTypeParameter newTypeParameter = newTypeParameters.next();
478 final PsiTypeParameter oldTypeParameter = oldTypeParameters.next();
479 substitutor = substitutor.put(newTypeParameter, resolveResult.getSubstitutor().substitute(oldTypeParameter));
481 return substitutor;
484 return PsiSubstitutor.EMPTY;
487 private void substituteMethodTypeParams(PsiElement scope, final PsiSubstitutor substitutor) {
488 scope.accept(new JavaRecursiveElementVisitor() {
489 @Override public void visitTypeElement(PsiTypeElement typeElement) {
490 PsiType type = typeElement.getType();
492 if (type instanceof PsiClassType) {
493 JavaResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
494 PsiElement resolved = resolveResult.getElement();
495 if (resolved instanceof PsiTypeParameter && ((PsiTypeParameter)resolved).getOwner() == myMethodCopy) {
496 PsiType newType = resolveResult.getSubstitutor().putAll(substitutor).substitute((PsiTypeParameter)resolved);
497 try {
498 typeElement.replace(myFactory.createTypeElement(newType));
499 return;
501 catch (IncorrectOperationException e) {
502 LOG.error(e);
506 super.visitTypeElement(typeElement);
511 private boolean isStrictlyFinal(PsiParameter parameter) {
512 for (PsiReference reference : ReferencesSearch.search(parameter, GlobalSearchScope.projectScope(myProject), false)) {
513 final PsiElement refElement = reference.getElement();
514 final PsiElement anonymousClass = PsiTreeUtil.getParentOfType(refElement, PsiAnonymousClass.class);
515 if (anonymousClass != null && PsiTreeUtil.isAncestor(myMethod, anonymousClass, true)) {
516 return true;
519 return false;
523 private boolean syncNeeded(final PsiReferenceExpression ref) {
524 if (!myMethod.hasModifierProperty(PsiModifier.SYNCHRONIZED)) return false;
525 final PsiMethod containingMethod = Util.getContainingMethod(ref);
526 if (containingMethod == null) return true;
527 if (!containingMethod.hasModifierProperty(PsiModifier.SYNCHRONIZED)) return true;
528 final PsiClass sourceContainingClass = myMethod.getContainingClass();
529 final PsiClass targetContainingClass = containingMethod.getContainingClass();
530 return !sourceContainingClass.equals(targetContainingClass);
533 private BlockData prepareBlock(PsiReferenceExpression ref,
534 final PsiSubstitutor callSubstitutor,
535 final PsiExpressionList argumentList,
536 final InlineMethodHandler.TailCallType tailCallType)
537 throws IncorrectOperationException {
538 final PsiCodeBlock block = myMethodCopy.getBody();
539 final PsiStatement[] originalStatements = block.getStatements();
541 PsiLocalVariable resultVar = null;
542 PsiType returnType = callSubstitutor.substitute(myMethod.getReturnType());
543 String resultName = null;
544 final int applicabilityLevel = PsiUtil.getApplicabilityLevel(myMethod, callSubstitutor, argumentList);
545 if (returnType != null && returnType != PsiType.VOID && tailCallType == InlineMethodHandler.TailCallType.None) {
546 resultName = myJavaCodeStyle.propertyNameToVariableName("result", VariableKind.LOCAL_VARIABLE);
547 resultName = myJavaCodeStyle.suggestUniqueVariableName(resultName, block.getFirstChild(), true);
548 PsiDeclarationStatement declaration = myFactory.createVariableDeclarationStatement(resultName, returnType, null);
549 declaration = (PsiDeclarationStatement)block.addAfter(declaration, null);
550 resultVar = (PsiLocalVariable)declaration.getDeclaredElements()[0];
553 PsiParameter[] parms = myMethodCopy.getParameterList().getParameters();
554 PsiLocalVariable[] parmVars = new PsiLocalVariable[parms.length];
555 for (int i = parms.length - 1; i >= 0; i--) {
556 PsiParameter parm = parms[i];
557 String parmName = parm.getName();
558 String name = parmName;
559 name = myJavaCodeStyle.variableNameToPropertyName(name, VariableKind.PARAMETER);
560 name = myJavaCodeStyle.propertyNameToVariableName(name, VariableKind.LOCAL_VARIABLE);
561 if (!name.equals(parmName)) {
562 name = myJavaCodeStyle.suggestUniqueVariableName(name, block.getFirstChild(), true);
564 RefactoringUtil.renameVariableReferences(parm, name, GlobalSearchScope.projectScope(myProject));
565 PsiType paramType = parm.getType();
566 @NonNls String defaultValue;
567 if (paramType instanceof PsiEllipsisType) {
568 final PsiEllipsisType ellipsisType = (PsiEllipsisType)paramType;
569 paramType = ellipsisType.toArrayType();
570 if (applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS) {
571 defaultValue = "new " + ellipsisType.getComponentType().getCanonicalText() + "[]{}";
573 else {
574 defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
577 else {
578 defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
581 PsiExpression initializer = myFactory.createExpressionFromText(defaultValue, null);
582 PsiDeclarationStatement declaration =
583 myFactory.createVariableDeclarationStatement(name, callSubstitutor.substitute(paramType), initializer);
584 declaration = (PsiDeclarationStatement)block.addAfter(declaration, null);
585 parmVars[i] = (PsiLocalVariable)declaration.getDeclaredElements()[0];
586 PsiUtil.setModifierProperty(parmVars[i], PsiModifier.FINAL, parm.hasModifierProperty(PsiModifier.FINAL));
589 PsiLocalVariable thisVar = null;
590 if (!myMethod.hasModifierProperty(PsiModifier.STATIC)) {
591 PsiClass containingClass = myMethod.getContainingClass();
593 if (containingClass != null) {
594 PsiType thisType = myFactory.createType(containingClass, callSubstitutor);
595 String[] names = myJavaCodeStyle.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, thisType)
596 .names;
597 String thisVarName = names[0];
598 thisVarName = myJavaCodeStyle.suggestUniqueVariableName(thisVarName, block.getFirstChild(), true);
599 PsiExpression initializer = myFactory.createExpressionFromText("null", null);
600 PsiDeclarationStatement declaration = myFactory.createVariableDeclarationStatement(thisVarName, thisType, initializer);
601 declaration = (PsiDeclarationStatement)block.addAfter(declaration, null);
602 thisVar = (PsiLocalVariable)declaration.getDeclaredElements()[0];
606 if (thisVar != null && syncNeeded(ref)) {
607 PsiSynchronizedStatement synchronizedStatement =
608 (PsiSynchronizedStatement)myFactory.createStatementFromText("synchronized(" + thisVar.getName() + "){}", block);
609 synchronizedStatement = (PsiSynchronizedStatement)CodeStyleManager.getInstance(myProject).reformat(synchronizedStatement);
610 synchronizedStatement = (PsiSynchronizedStatement)block.add(synchronizedStatement);
611 final PsiCodeBlock synchronizedBody = synchronizedStatement.getBody();
612 for (final PsiStatement originalStatement : originalStatements) {
613 synchronizedBody.add(originalStatement);
614 originalStatement.delete();
618 if (resultName != null || tailCallType == InlineMethodHandler.TailCallType.Simple) {
619 PsiReturnStatement[] returnStatements = RefactoringUtil.findReturnStatements(myMethodCopy);
620 for (PsiReturnStatement returnStatement : returnStatements) {
621 final PsiExpression returnValue = returnStatement.getReturnValue();
622 if (returnValue == null) continue;
623 PsiStatement statement;
624 if (tailCallType == InlineMethodHandler.TailCallType.Simple) {
625 if (returnValue instanceof PsiCallExpression) {
626 PsiExpressionStatement exprStatement = (PsiExpressionStatement) myFactory.createStatementFromText("a;", null);
627 exprStatement.getExpression().replace(returnValue);
628 returnStatement.getParent().addBefore(exprStatement, returnStatement);
630 statement = myFactory.createStatementFromText("return;", null);
632 else {
633 statement = myFactory.createStatementFromText(resultName + "=0;", null);
634 statement = (PsiStatement)myCodeStyleManager.reformat(statement);
635 PsiAssignmentExpression assignment = (PsiAssignmentExpression)((PsiExpressionStatement)statement).getExpression();
636 assignment.getRExpression().replace(returnValue);
638 returnStatement.replace(statement);
642 return new BlockData(block, thisVar, parmVars, resultVar);
645 private void solveVariableNameConflicts(PsiElement scope, final PsiElement placeToInsert) throws IncorrectOperationException {
646 if (scope instanceof PsiVariable) {
647 PsiVariable var = (PsiVariable)scope;
648 String name = var.getName();
649 String oldName = name;
650 while (true) {
651 String newName = myJavaCodeStyle.suggestUniqueVariableName(name, placeToInsert, true);
652 if (newName.equals(name)) break;
653 name = newName;
654 newName = myJavaCodeStyle.suggestUniqueVariableName(name, var, true);
655 if (newName.equals(name)) break;
656 name = newName;
658 if (!name.equals(oldName)) {
659 RefactoringUtil.renameVariableReferences(var, name, GlobalSearchScope.projectScope(myProject));
660 var.getNameIdentifier().replace(myFactory.createIdentifier(name));
664 PsiElement[] children = scope.getChildren();
665 for (PsiElement child : children) {
666 solveVariableNameConflicts(child, placeToInsert);
670 private void addParmAndThisVarInitializers(BlockData blockData, PsiMethodCallExpression methodCall) throws IncorrectOperationException {
671 PsiExpression[] args = methodCall.getArgumentList().getExpressions();
672 for (int i = 0; i < args.length; i++) {
673 int j = Math.min(i, blockData.parmVars.length - 1);
674 final PsiExpression initializer = blockData.parmVars[j].getInitializer();
675 LOG.assertTrue(initializer != null);
676 if (initializer instanceof PsiNewExpression && ((PsiNewExpression)initializer).getArrayInitializer() != null) { //varargs initializer
677 final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)initializer).getArrayInitializer();
678 arrayInitializer.add(args[i]);
679 continue;
682 initializer.replace(args[i]);
685 if (blockData.thisVar != null) {
686 PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
687 if (qualifier == null) {
688 PsiElement parent = methodCall.getParent();
689 while (true) {
690 if (parent instanceof PsiClass) break;
691 if (parent instanceof PsiFile) break;
692 parent = parent.getParent();
694 if (parent instanceof PsiClass) {
695 final PsiClass parentClass = (PsiClass)parent;
696 final PsiClass containingClass = myMethod.getContainingClass();
697 if (InheritanceUtil.isInheritorOrSelf(parentClass, containingClass, true)) {
698 qualifier = myFactory.createExpressionFromText("this", null);
700 else {
701 String name = containingClass.getName();
702 if (name != null) {
703 qualifier = myFactory.createExpressionFromText(name + ".this", null);
705 else { //?
706 qualifier = myFactory.createExpressionFromText("this", null);
710 else {
711 qualifier = myFactory.createExpressionFromText("this", null);
714 else if (qualifier instanceof PsiSuperExpression) {
715 qualifier = myFactory.createExpressionFromText("this", null);
717 blockData.thisVar.getInitializer().replace(qualifier);
721 private boolean canInlineParmOrThisVariable(PsiLocalVariable variable) {
722 boolean isAccessedForWriting = false;
723 for (PsiReference ref : ReferencesSearch.search(variable)) {
724 PsiElement refElement = ref.getElement();
725 if (refElement instanceof PsiExpression) {
726 if (PsiUtil.isAccessedForWriting((PsiExpression)refElement)) {
727 isAccessedForWriting = true;
732 PsiExpression initializer = variable.getInitializer();
733 boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && false;
734 return canInlineParmOrThisVariable(initializer, shouldBeFinal, false, ReferencesSearch.search(variable).findAll().size(), isAccessedForWriting);
737 private void inlineParmOrThisVariable(PsiLocalVariable variable, boolean strictlyFinal) throws IncorrectOperationException {
738 PsiReference firstRef = ReferencesSearch.search(variable).findFirst();
740 if (firstRef == null) {
741 variable.getParent().delete(); //Q: side effects?
742 return;
746 boolean isAccessedForWriting = false;
747 final Collection<PsiReference> refs = ReferencesSearch.search(variable).findAll();
748 for (PsiReference ref : refs) {
749 PsiElement refElement = ref.getElement();
750 if (refElement instanceof PsiExpression) {
751 if (PsiUtil.isAccessedForWriting((PsiExpression)refElement)) {
752 isAccessedForWriting = true;
757 PsiExpression initializer = variable.getInitializer();
758 boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && strictlyFinal;
759 if (canInlineParmOrThisVariable(initializer, shouldBeFinal, strictlyFinal, refs.size(), isAccessedForWriting)) {
760 if (shouldBeFinal) {
761 declareUsedLocalsFinal(initializer, strictlyFinal);
763 for (PsiReference ref : refs) {
764 final PsiJavaCodeReferenceElement javaRef = (PsiJavaCodeReferenceElement)ref;
765 if (initializer instanceof PsiThisExpression && ((PsiThisExpression)initializer).getQualifier() == null) {
766 final PsiClass varThisClass = RefactoringUtil.getThisClass(variable);
767 if (RefactoringUtil.getThisClass(javaRef) != varThisClass) {
768 initializer = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createExpressionFromText(varThisClass.getName() + ".this", variable);
772 PsiExpression expr = InlineUtil.inlineVariable(variable, initializer, javaRef);
774 //Q: move the following code to some util? (addition to inline?)
775 if (expr instanceof PsiThisExpression) {
776 if (expr.getParent() instanceof PsiReferenceExpression) {
777 PsiReferenceExpression refExpr = (PsiReferenceExpression)expr.getParent();
778 PsiElement refElement = refExpr.resolve();
779 PsiExpression exprCopy = (PsiExpression)refExpr.copy();
780 refExpr = (PsiReferenceExpression)refExpr.replace(myFactory.createExpressionFromText(refExpr.getReferenceName(), null));
781 if (refElement != null) {
782 PsiElement newRefElement = refExpr.resolve();
783 if (!refElement.equals(newRefElement)) {
784 // change back
785 refExpr.replace(exprCopy);
791 variable.getParent().delete();
795 private boolean canInlineParmOrThisVariable(PsiExpression initializer,
796 boolean shouldBeFinal,
797 boolean strictlyFinal,
798 int accessCount,
799 boolean isAccessedForWriting) {
800 if (strictlyFinal) {
801 class CanAllLocalsBeDeclaredFinal extends JavaRecursiveElementWalkingVisitor {
802 boolean success = true;
804 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
805 final PsiElement psiElement = expression.resolve();
806 if (psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) {
807 if (!RefactoringUtil.canBeDeclaredFinal((PsiVariable)psiElement)) {
808 success = false;
813 @Override public void visitElement(PsiElement element) {
814 if (success) {
815 super.visitElement(element);
820 final CanAllLocalsBeDeclaredFinal canAllLocalsBeDeclaredFinal = new CanAllLocalsBeDeclaredFinal();
821 initializer.accept(canAllLocalsBeDeclaredFinal);
822 if (!canAllLocalsBeDeclaredFinal.success) return false;
824 if (initializer instanceof PsiReferenceExpression) {
825 PsiVariable refVar = (PsiVariable)((PsiReferenceExpression)initializer).resolve();
826 if (refVar == null) {
827 return !isAccessedForWriting;
829 if (refVar instanceof PsiField) {
830 if (isAccessedForWriting) return false;
832 PsiField field = (PsiField)refVar;
833 if (isFieldNonModifiable(field)){
834 return true;
836 //TODO: other cases
837 return false;
839 return true; //TODO: "suspicous" places to review by user!
841 else {
842 if (isAccessedForWriting) {
843 if (refVar.hasModifierProperty(PsiModifier.FINAL) || shouldBeFinal) return false;
844 PsiReference[] refs =
845 ReferencesSearch.search(refVar, GlobalSearchScope.projectScope(myProject), false).toArray(new PsiReference[0]);
846 return refs.length == 1; //TODO: control flow
848 else {
849 if (shouldBeFinal) {
850 return refVar.hasModifierProperty(PsiModifier.FINAL) || RefactoringUtil.canBeDeclaredFinal(refVar);
852 return true;
856 else if (isAccessedForWriting) {
857 return false;
859 else if (initializer instanceof PsiCallExpression) {
860 if (accessCount > 1) return false;
861 final PsiExpressionList argumentList = ((PsiCallExpression)initializer).getArgumentList();
862 if (argumentList == null) return false;
863 final PsiExpression[] expressions = argumentList.getExpressions();
864 for (PsiExpression expression : expressions) {
865 if (!canInlineParmOrThisVariable(expression, shouldBeFinal, strictlyFinal, accessCount, false)) {
866 return false;
869 return true; //TODO: "suspicous" places to review by user!
871 else if (initializer instanceof PsiLiteralExpression) {
872 return true;
874 else if (initializer instanceof PsiArrayAccessExpression) {
875 final PsiExpression arrayExpression = ((PsiArrayAccessExpression)initializer).getArrayExpression();
876 final PsiExpression indexExpression = ((PsiArrayAccessExpression)initializer).getIndexExpression();
877 return canInlineParmOrThisVariable(arrayExpression, shouldBeFinal, strictlyFinal, accessCount, false) &&
878 canInlineParmOrThisVariable(indexExpression, shouldBeFinal, strictlyFinal, accessCount, false);
880 else if (initializer instanceof PsiParenthesizedExpression) {
881 PsiExpression expr = ((PsiParenthesizedExpression)initializer).getExpression();
882 return expr == null || canInlineParmOrThisVariable(expr, shouldBeFinal, strictlyFinal, accessCount, false);
884 else if (initializer instanceof PsiTypeCastExpression) {
885 PsiExpression operand = ((PsiTypeCastExpression)initializer).getOperand();
886 return operand != null && canInlineParmOrThisVariable(operand, shouldBeFinal, strictlyFinal, accessCount, false);
888 else if (initializer instanceof PsiBinaryExpression) {
889 PsiBinaryExpression binExpr = (PsiBinaryExpression)initializer;
890 PsiExpression lOperand = binExpr.getLOperand();
891 PsiExpression rOperand = binExpr.getROperand();
892 return rOperand != null && canInlineParmOrThisVariable(lOperand, shouldBeFinal, strictlyFinal, accessCount, false) &&
893 canInlineParmOrThisVariable(rOperand, shouldBeFinal, strictlyFinal, accessCount, false);
895 else if (initializer instanceof PsiClassObjectAccessExpression) {
896 return true;
898 else if (initializer instanceof PsiThisExpression) {
899 return true;
901 else if (initializer instanceof PsiSuperExpression) {
902 return true;
904 else {
905 return false;
909 private static void declareUsedLocalsFinal(PsiElement expr, boolean strictlyFinal) throws IncorrectOperationException {
910 if (expr instanceof PsiReferenceExpression) {
911 PsiElement refElement = ((PsiReferenceExpression)expr).resolve();
912 if (refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) {
913 if (strictlyFinal || RefactoringUtil.canBeDeclaredFinal((PsiVariable)refElement)) {
914 PsiUtil.setModifierProperty(((PsiVariable)refElement), PsiModifier.FINAL, true);
918 PsiElement[] children = expr.getChildren();
919 for (PsiElement child : children) {
920 declareUsedLocalsFinal(child, strictlyFinal);
925 private boolean isFieldNonModifiable(PsiField field) {
926 if (field.hasModifierProperty(PsiModifier.FINAL)){
927 return true;
929 PsiElement[] refs = myManager.getSearchHelper().findReferences(field, null, false);
930 for(int i = 0; i < refs.length; i++){
931 PsiReferenceExpression ref = (PsiReferenceExpression)refs[i];
932 if (PsiUtil.isAccessedForWriting(ref)) {
933 PsiElement container = ref.getParent();
934 while(true){
935 if (container instanceof PsiMethod ||
936 container instanceof PsiField ||
937 container instanceof PsiClassInitializer ||
938 container instanceof PsiFile) break;
939 container = container.getParent();
941 if (container instanceof PsiMethod && ((PsiMethod)container).isConstructor()) continue;
942 return false;
945 return true;
949 private void inlineResultVariable(PsiVariable resultVar) throws IncorrectOperationException {
950 PsiAssignmentExpression assignment = null;
951 PsiReferenceExpression resultUsage = null;
952 for (PsiReference ref1 : ReferencesSearch.search(resultVar, GlobalSearchScope.projectScope(myProject), false)) {
953 PsiReferenceExpression ref = (PsiReferenceExpression)ref1;
954 if (ref.getParent() instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)ref.getParent()).getLExpression().equals(ref)) {
955 if (assignment != null) {
956 assignment = null;
957 break;
959 else {
960 assignment = (PsiAssignmentExpression)ref.getParent();
963 else {
964 LOG.assertTrue(resultUsage == null);
965 resultUsage = ref;
969 if (assignment == null) return;
970 boolean condition = assignment.getParent() instanceof PsiExpressionStatement;
971 LOG.assertTrue(condition);
972 // SCR3175 fixed: inline only if declaration and assignment is in the same code block.
973 if (!(assignment.getParent().getParent() == resultVar.getParent().getParent())) return;
974 if (resultUsage != null) {
975 String name = resultVar.getName();
976 PsiDeclarationStatement declaration =
977 myFactory.createVariableDeclarationStatement(name, resultVar.getType(), assignment.getRExpression());
978 declaration = (PsiDeclarationStatement)assignment.getParent().replace(declaration);
979 resultVar.getParent().delete();
980 resultVar = (PsiVariable)declaration.getDeclaredElements()[0];
982 PsiElement parentStatement = RefactoringUtil.getParentStatement(resultUsage, true);
983 PsiElement next = declaration.getNextSibling();
984 boolean canInline = false;
985 while (true) {
986 if (next == null) break;
987 if (parentStatement.equals(next)) {
988 canInline = true;
989 break;
991 if (next instanceof PsiStatement) break;
992 next = next.getNextSibling();
995 if (canInline) {
996 InlineUtil.inlineVariable(resultVar, resultVar.getInitializer(), resultUsage);
997 declaration.delete();
1000 else {
1001 PsiExpression rExpression = assignment.getRExpression();
1002 while (rExpression instanceof PsiReferenceExpression) rExpression = ((PsiReferenceExpression)rExpression).getQualifierExpression();
1003 if (rExpression == null || !PsiUtil.isStatement(rExpression)) {
1004 assignment.delete();
1006 else {
1007 assignment.replace(rExpression);
1009 resultVar.delete();
1013 private static final Key<String> MARK_KEY = Key.create("");
1015 private PsiReferenceExpression[] addBracesWhenNeeded(PsiReferenceExpression[] refs) throws IncorrectOperationException {
1016 ArrayList<PsiReferenceExpression> refsVector = new ArrayList<PsiReferenceExpression>();
1017 ArrayList<PsiBlockStatement> addedBracesVector = new ArrayList<PsiBlockStatement>();
1018 myAddedClassInitializers = new HashMap<PsiField, PsiClassInitializer>();
1020 for (PsiReferenceExpression ref : refs) {
1021 ref.putCopyableUserData(MARK_KEY, "");
1024 RefLoop:
1025 for (PsiReferenceExpression ref : refs) {
1026 if (!ref.isValid()) continue;
1028 PsiElement parentStatement = RefactoringUtil.getParentStatement(ref, true);
1029 if (parentStatement != null) {
1030 PsiElement parent = ref.getParent();
1031 while (!parent.equals(parentStatement)) {
1032 if (parent instanceof PsiStatement && !(parent instanceof PsiDeclarationStatement)) {
1033 String text = "{\n}";
1034 PsiBlockStatement blockStatement = (PsiBlockStatement)myFactory.createStatementFromText(text, null);
1035 blockStatement = (PsiBlockStatement)myCodeStyleManager.reformat(blockStatement);
1036 blockStatement.getCodeBlock().add(parent);
1037 blockStatement = (PsiBlockStatement)parent.replace(blockStatement);
1039 PsiElement newStatement = blockStatement.getCodeBlock().getStatements()[0];
1040 addMarkedElements(refsVector, newStatement);
1041 addedBracesVector.add(blockStatement);
1042 continue RefLoop;
1044 parent = parent.getParent();
1047 else {
1048 final PsiField field = PsiTreeUtil.getParentOfType(ref, PsiField.class);
1049 if (field != null) {
1050 if (field instanceof PsiEnumConstant) {
1051 inlineEnumConstantParameter(refsVector, ref);
1052 continue;
1054 field.normalizeDeclaration();
1055 final PsiExpression initializer = field.getInitializer();
1056 LOG.assertTrue(initializer != null);
1057 PsiClassInitializer classInitializer = myFactory.createClassInitializer();
1058 final PsiClass containingClass = field.getContainingClass();
1059 classInitializer = (PsiClassInitializer)containingClass.addAfter(classInitializer, field);
1060 containingClass.addAfter(CodeEditUtil.createLineFeed(field.getManager()), field);
1061 final PsiCodeBlock body = classInitializer.getBody();
1062 PsiExpressionStatement statement = (PsiExpressionStatement)myFactory.createStatementFromText(field.getName() + " = 0;", body);
1063 statement = (PsiExpressionStatement)body.add(statement);
1064 final PsiAssignmentExpression assignment = (PsiAssignmentExpression)statement.getExpression();
1065 assignment.getLExpression().replace(RenameJavaVariableProcessor.createMemberReference(field, assignment));
1066 assignment.getRExpression().replace(initializer);
1067 addMarkedElements(refsVector, statement);
1068 if (field.hasModifierProperty(PsiModifier.STATIC)) {
1069 PsiUtil.setModifierProperty(classInitializer, PsiModifier.STATIC, true);
1071 myAddedClassInitializers.put(field, classInitializer);
1072 continue;
1076 refsVector.add(ref);
1079 for (PsiReferenceExpression ref : refs) {
1080 ref.putCopyableUserData(MARK_KEY, null);
1083 myAddedBraces = addedBracesVector.toArray(new PsiBlockStatement[addedBracesVector.size()]);
1084 return refsVector.toArray(new PsiReferenceExpression[refsVector.size()]);
1087 private void inlineEnumConstantParameter(final List<PsiReferenceExpression> refsVector,
1088 final PsiReferenceExpression ref) throws IncorrectOperationException {
1089 PsiExpression expr = getSimpleReturnedExpression(myMethod);
1090 if (expr != null) {
1091 refsVector.add(ref);
1093 else {
1094 PsiCall call = PsiTreeUtil.getParentOfType(ref, PsiCall.class);
1095 @NonNls String text = "new Object() { " + myMethod.getReturnTypeElement().getText() + " evaluate() { return " + call.getText() + ";}}.evaluate";
1096 PsiExpression callExpr = JavaPsiFacade.getInstance(myProject).getParserFacade().createExpressionFromText(text, call);
1097 PsiElement classExpr = ref.replace(callExpr);
1098 classExpr.accept(new JavaRecursiveElementWalkingVisitor() {
1099 public void visitReturnStatement(final PsiReturnStatement statement) {
1100 super.visitReturnStatement(statement);
1101 PsiExpression expr = statement.getReturnValue();
1102 if (expr instanceof PsiMethodCallExpression) {
1103 refsVector.add(((PsiMethodCallExpression) expr).getMethodExpression());
1107 if (classExpr.getParent() instanceof PsiMethodCallExpression) {
1108 PsiExpressionList args = ((PsiMethodCallExpression)classExpr.getParent()).getArgumentList();
1109 PsiExpression[] argExpressions = args.getExpressions();
1110 if (argExpressions.length > 0) {
1111 args.deleteChildRange(argExpressions [0], argExpressions [argExpressions.length-1]);
1117 @Nullable
1118 private static PsiExpression getSimpleReturnedExpression(final PsiMethod method) {
1119 PsiCodeBlock body = method.getBody();
1120 if (body == null) return null;
1121 PsiStatement[] psiStatements = body.getStatements();
1122 if (psiStatements.length != 1) return null;
1123 PsiStatement statement = psiStatements[0];
1124 if (!(statement instanceof PsiReturnStatement)) return null;
1125 return ((PsiReturnStatement) statement).getReturnValue();
1128 private static void addMarkedElements(final List<PsiReferenceExpression> array, PsiElement scope) {
1129 scope.accept(new PsiRecursiveElementWalkingVisitor() {
1130 @Override public void visitElement(PsiElement element) {
1131 if (element.getCopyableUserData(MARK_KEY) != null) {
1132 array.add((PsiReferenceExpression)element);
1133 element.putCopyableUserData(MARK_KEY, null);
1135 super.visitElement(element);
1140 private void removeAddedBracesWhenPossible() throws IncorrectOperationException {
1141 if (myAddedBraces == null) return;
1143 for (PsiBlockStatement blockStatement : myAddedBraces) {
1144 PsiStatement[] statements = blockStatement.getCodeBlock().getStatements();
1145 if (statements.length == 1) {
1146 blockStatement.replace(statements[0]);
1150 final Set<PsiField> fields = myAddedClassInitializers.keySet();
1152 for (PsiField psiField : fields) {
1153 final PsiClassInitializer classInitializer = myAddedClassInitializers.get(psiField);
1154 final PsiExpression initializer = getSimpleFieldInitializer(psiField, classInitializer);
1155 if (initializer != null) {
1156 psiField.getInitializer().replace(initializer);
1157 classInitializer.delete();
1159 else {
1160 psiField.getInitializer().delete();
1165 @Nullable
1166 private PsiExpression getSimpleFieldInitializer(PsiField field, PsiClassInitializer initializer) {
1167 final PsiStatement[] statements = initializer.getBody().getStatements();
1168 if (statements.length != 1) return null;
1169 if (!(statements[0] instanceof PsiExpressionStatement)) return null;
1170 final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
1171 if (!(expression instanceof PsiAssignmentExpression)) return null;
1172 final PsiExpression lExpression = ((PsiAssignmentExpression)expression).getLExpression();
1173 if (!(lExpression instanceof PsiReferenceExpression)) return null;
1174 final PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
1175 if (!myManager.areElementsEquivalent(field, resolved)) return null;
1176 return ((PsiAssignmentExpression)expression).getRExpression();
1179 public static boolean checkBadReturns(PsiMethod method) {
1180 PsiReturnStatement[] returns = RefactoringUtil.findReturnStatements(method);
1181 if (returns.length == 0) return false;
1182 PsiCodeBlock body = method.getBody();
1183 ControlFlow controlFlow;
1184 try {
1185 controlFlow = ControlFlowFactory.getInstance(body.getProject()).getControlFlow(body, new LocalsControlFlowPolicy(body), false);
1187 catch (AnalysisCanceledException e) {
1188 return false;
1190 if (LOG.isDebugEnabled()) {
1191 LOG.debug("Control flow:");
1192 LOG.debug(controlFlow.toString());
1195 List<Instruction> instructions = new ArrayList<Instruction>(controlFlow.getInstructions());
1197 // temporary replace all return's with empty statements in the flow
1198 for (PsiReturnStatement aReturn : returns) {
1199 int offset = controlFlow.getStartOffset(aReturn);
1200 int endOffset = controlFlow.getEndOffset(aReturn);
1201 while (offset <= endOffset && !(instructions.get(offset) instanceof GoToInstruction)) {
1202 offset++;
1204 LOG.assertTrue(instructions.get(offset) instanceof GoToInstruction);
1205 instructions.set(offset, EmptyInstruction.INSTANCE);
1208 for (PsiReturnStatement aReturn : returns) {
1209 int offset = controlFlow.getEndOffset(aReturn);
1210 while (true) {
1211 if (offset == instructions.size()) break;
1212 Instruction instruction = instructions.get(offset);
1213 if (instruction instanceof GoToInstruction) {
1214 offset = ((GoToInstruction)instruction).offset;
1216 else if (instruction instanceof ThrowToInstruction) {
1217 offset = ((ThrowToInstruction)instruction).offset;
1219 else if (instruction instanceof ConditionalThrowToInstruction) {
1220 // In case of "conditional throw to", control flow will not be altered
1221 // If exception handler is in method, we will inline it to ivokation site
1222 // If exception handler is at invocation site, execution will continue to get there
1223 offset++;
1225 else {
1226 return true;
1231 return false;
1234 private static class BlockData {
1235 final PsiCodeBlock block;
1236 final PsiLocalVariable thisVar;
1237 final PsiLocalVariable[] parmVars;
1238 final PsiLocalVariable resultVar;
1240 public BlockData(PsiCodeBlock block, PsiLocalVariable thisVar, PsiLocalVariable[] parmVars, PsiLocalVariable resultVar) {
1241 this.block = block;
1242 this.thisVar = thisVar;
1243 this.parmVars = parmVars;
1244 this.resultVar = resultVar;
1248 @NotNull
1249 protected Collection<? extends PsiElement> getElementsToWrite(@NotNull final UsageViewDescriptor descriptor) {
1250 if (myInlineThisOnly) {
1251 return Collections.singletonList(myReference);
1253 else {
1254 return myReference == null ? Collections.singletonList(myMethod) : Arrays.asList(myReference, myMethod);